package cn.turboinfo.fuyang.api.provider.common.service.impl.product;

import cn.turboinfo.fuyang.api.domain.common.service.file.FileAttachmentService;
import cn.turboinfo.fuyang.api.domain.common.service.product.ProductService;
import cn.turboinfo.fuyang.api.domain.common.service.product.ProductSkuService;
import cn.turboinfo.fuyang.api.domain.common.service.spec.SpecSetRelService;
import cn.turboinfo.fuyang.api.domain.util.GeoUtils;
import cn.turboinfo.fuyang.api.domain.web.component.file.FileRefTypeConstant;
import cn.turboinfo.fuyang.api.entity.common.enumeration.common.EntityObjectType;
import cn.turboinfo.fuyang.api.entity.common.enumeration.product.ProductSkuStatus;
import cn.turboinfo.fuyang.api.entity.common.enumeration.product.ProductStatus;
import cn.turboinfo.fuyang.api.entity.common.exception.product.ProductException;
import cn.turboinfo.fuyang.api.entity.common.pojo.file.FileAttachment;
import cn.turboinfo.fuyang.api.entity.common.pojo.product.*;
import cn.turboinfo.fuyang.api.entity.common.pojo.spec.SpecSetRelCreator;
import cn.turboinfo.fuyang.api.provider.common.constant.GeoConstants;
import cn.turboinfo.fuyang.api.provider.common.repository.database.product.ProductDAO;
import cn.turboinfo.fuyang.api.provider.common.repository.database.product.ProductPO;
import cn.turboinfo.fuyang.api.provider.common.service.geo.GeoPosition;
import cn.turboinfo.fuyang.api.provider.common.service.geo.GeoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanCreatorHelper;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanUpdaterHelper;
import net.sunshow.toolkit.core.qbean.helper.service.impl.DefaultQServiceImpl;
import nxcloud.foundation.core.data.support.annotation.EnableSoftDelete;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@RequiredArgsConstructor
@EnableSoftDelete
@Service
public class ProductServiceImpl extends DefaultQServiceImpl<Product, Long, ProductPO, ProductDAO> implements ProductService {

    private final FileAttachmentService fileAttachmentService;
    private final SpecSetRelService specSetRelService;
    private final ProductSkuService productSkuService;

    private final GeoService geoService;

    @Override
    @Transactional
    public Product save(ProductCreator creator) throws ProductException {
        ProductPO productPO = new ProductPO();
        QBeanCreatorHelper.copyCreatorField(productPO, creator);
        Product product = convertQBean(dao.save(productPO));
        //保存附件关系
        fileAttachmentService.updateRefId(Stream.of(creator.getImgList(), creator.getVideoList()).flatMap(Collection::stream).collect(Collectors.toList()), product.getId());
        //保存属性组关联关系
        specSetRelService.save(SpecSetRelCreator.builder().withObjectId(product.getId()).withSpecSetId(creator.getSpecSetId()).withObjectType(EntityObjectType.PRODUCT).build());
        //保存sku及关联关系
        creator.getProductSkuList().forEach(productSku -> {
            productSku.setId(productSkuService.save(ProductSkuCreator.builder().withSpecList(productSku.getSpecList()).
                    withOriginalPrice(productSku.getOriginalPrice()).withPrice(productSku.getPrice()).withProductId(product.getId()).withStatus(ProductSkuStatus.DEFAULT).build()).getId());
        });
        return product;
    }

    @Override
    @Transactional
    public void updateStatus(Long id, ProductStatus productStatus) throws ProductException {
        ProductPO productPO = getEntityWithNullCheckForUpdate(id);
        productPO.setStatus(productStatus);
    }

    @Override
    @Transactional
    public void updateCreditScore(Long id, BigDecimal creditScore) {
        ProductPO po = getEntityWithNullCheckForUpdate(id);
        po.setCreditScore(creditScore);
    }

    @Override
    @Transactional
    public void updateOrderNum(Long id, Long orderNum) {
        ProductPO po = getEntityWithNullCheckForUpdate(id);
        po.setOrderNum(orderNum);
    }

    @Override
    public List<Product> findByShopIdCollection(Collection<Long> shopIdCollection) {
        return convertQBeanToList(dao.findByShopIdInAndStatus(shopIdCollection, ProductStatus.PUBLISHED));
    }

    @Override
    public List<Product> findByCompanyId(Long companyId) {
        return convertQBeanToList(dao.findByCompanyIdAndStatusOrderByUpdatedTimeDesc(companyId, ProductStatus.PUBLISHED));
    }

    @Override
    @Transactional
    public Product update(ProductUpdater updater) throws ProductException {
        ProductPO productPO = getEntityWithNullCheckForUpdate(updater.getUpdateId(), dao);
        QBeanUpdaterHelper.copyUpdaterField(productPO, updater);
        //删除原附件
        List<Long> uploadFileList = Stream.of(updater.getImgList(), updater.getVideoList()).flatMap(Collection::stream).collect(Collectors.toList());
        Map<Long, List<Long>> uploadFileMap = uploadFileList.stream().collect(Collectors.groupingBy(a -> a));

        List<Long> exitsFileIdList = fileAttachmentService.findByRefAndRefType(updater.getUpdateId(), List.of(new String[]{FileRefTypeConstant.PRODUCT_IMG, FileRefTypeConstant.PRODUCT_VIDEO}))
                .stream()
                .map(FileAttachment::getId)
                .toList();

        List<Long> deleteFileList = exitsFileIdList.stream().filter(fileId -> !uploadFileMap.containsKey(fileId)).collect(Collectors.toList());
        if (!deleteFileList.isEmpty()) {
            fileAttachmentService.deleteByIdCollection(deleteFileList);
        }
        fileAttachmentService.updateRefId(uploadFileList, updater.getUpdateId());

        //更新sku(可能删除)
        val productSkuList = productSkuService.findSkuByProductId(updater.getUpdateId());
        val updateSkuMap = updater.getProductSkuList().stream().collect(Collectors.toMap(ProductSku::getId, Function.identity()));
        productSkuList.forEach(productSku -> {
            if (!updateSkuMap.containsKey(productSku.getId())) {
                productSkuService.deleteById(productSku.getId());
            }
        });
        updater.getProductSkuList().forEach(productSku -> {
            productSkuService.update(ProductSkuUpdater.builder(productSku.getId()).withOriginalPrice(productSku.getOriginalPrice()).withPrice(productSku.getPrice()).build());
        });
        return convertQBean(productPO);
    }

    @Override
    public long countByCompanyId(Long companyId) {
        return dao.countByCompanyIdAndStatus(companyId, ProductStatus.PUBLISHED);
    }

    @Override
    public Map<Long, BigDecimal> calculateDistance(Collection<Long> productIdCollection, BigDecimal longitude, BigDecimal latitude) {
        // 将产品ID转成字符串数组
        String[] productIds = productIdCollection.stream().map(String::valueOf).toArray(String[]::new);

        List<GeoPosition> positionList = geoService.get(GeoConstants.STORE_PRODUCT, productIds);

        // 按顺序映射到产品ID
        Map<Long, BigDecimal> distanceMap = new HashMap<>();
        for (int i = 0; i < productIds.length; i++) {
            GeoPosition position = positionList.get(i);
            if (position != null) {
                BigDecimal distance = GeoUtils.INSTANCE.calculateDistance(
                        longitude, latitude,
                        BigDecimal.valueOf(position.getLongitude()), BigDecimal.valueOf(position.getLatitude())
                );
                distanceMap.put(Long.valueOf(productIds[i]), distance);
            }
        }

        return distanceMap;
    }

    @Override
    public List<Product> findByNameContaining(String name) {
        return convertQBeanToList(dao.findByNameContaining(name));
    }

    @Override
    public List<Product> findByCategoryId(Long categoryId) {
        return convertQBeanToList(dao.findByCategoryIdAndStatusOrderByUpdatedTimeDesc(categoryId, ProductStatus.PUBLISHED));
    }
}
